﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net.Sockets;
using System.Net;
using System.Threading;

namespace ihm
{
    public class ClientSocket
    {
        //Argument passé au handler d'évènement
        public class NetworkEventArgs
        {
            public NetworkEventArgs(string _data) { Data = _data; }
            public String Data { get; private set; } // readonly
        }

        //Socket
        Socket myClientSocket;

        //Evénements utiles à la gestion des données du socket
        public delegate void DataArrivalHandler(Object sender, NetworkEventArgs e);
        public event DataArrivalHandler DataArrival;

        public delegate void ErrorHandler(Object sender, NetworkEventArgs e);
        public event ErrorHandler ErrorOccured;

        public delegate void DataSentHandler(Object sender, NetworkEventArgs e);
        public event DataSentHandler DataSent;

        public delegate void ConnectedHandler(Object sender, NetworkEventArgs e);
        public event ConnectedHandler Connected;

        public delegate void DisconnectedHandler(Object sender, NetworkEventArgs e);
        public event DisconnectedHandler Disconnected;

        //Thread permettant de checker en continue l'arrivée de données 
        Thread DataChecker;

        //Connexion
        public void Connect(string _ip, string _port)
        {
            try
            {
                IPAddress ip = IPAddress.Parse(GetFormattedAdrr(_ip));
                IPEndPoint ipEnd = new IPEndPoint(ip, Int32.Parse(_port));
                myClientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                myClientSocket.Connect(ipEnd);
                //Si on est connecté, on lance l'écoute sur le socket
                if (myClientSocket.Connected)
                {
                    if(this.Connected != null)
                        this.Connected(this, new NetworkEventArgs("Connected"));
                    this.DataChecker = new Thread(new ThreadStart(CheckData));
                    this.DataChecker.Start();
                }
                else
                    if (this.ErrorOccured != null)
                        this.ErrorOccured(this, new NetworkEventArgs("Connection error"));
            }
            catch
            {
                if (this.ErrorOccured != null)
                    this.ErrorOccured(this, new NetworkEventArgs("Connection error"));
            }
        }


        //Déconnexion
        public void Disconnect()
        {
            myClientSocket.Disconnect(false);
            if (!myClientSocket.Connected)
            {
                if(this.Disconnected != null)
                    this.Disconnected(this, new NetworkEventArgs("Disconnected"));
            }
            else
            {
                if(this.ErrorOccured != null)
                    this.ErrorOccured(this, new NetworkEventArgs("Disconnection error"));
            }
        }

        private String GetFormattedAdrr(string ip)
        {

            try
            {
                IPHostEntry iphostentry = Dns.GetHostByName(ip);
                String IPStr = "";
                foreach (IPAddress ipaddress in iphostentry.AddressList)
                {
                    IPStr = ipaddress.ToString();
                    return IPStr;
                }
            }
            catch (SocketException E)
            {
                Console.Out.WriteLine(E.Message);
            }
            return "";
        }

        //Envoyer un message
        public void SendMsg(string message)
        {
            byte[] msg = System.Text.Encoding.UTF8.GetBytes(message);
            int DataSentSize = myClientSocket.Send(msg, msg.Length, SocketFlags.None);
            if (DataSentSize == 0)
            {
                if(this.ErrorOccured != null)
                    this.ErrorOccured(this, new NetworkEventArgs("Send error: No data"));
            }
            else
            {
                if(this.DataSent != null)
                    this.DataSent(this, new NetworkEventArgs(message));
            }
        }

        //Booléen représentant la connexion
        public bool IsConnected()
        {
            return this.myClientSocket.Connected;
        }

        //Fonction de la tâche permettant de récupérer en continue les données envoyées par VxSend
        public void CheckData()
        {
            try
            {
                while (true)
                {
                    if (myClientSocket.Connected)
                    {
                        //On vérifie si le socket n'a pas été déconnecté par erreur
                        if(myClientSocket.Poll(10, SelectMode.SelectRead) && myClientSocket.Available == 0)
                        {
                            if(this.Disconnected != null)
                                Disconnected(this, new NetworkEventArgs("forcedDisconnect"));
                            Thread.CurrentThread.Abort();
                        }

                        //Si il y a des données
                        if (myClientSocket.Available > 0)
                        {
                            string messageReceived = null;
                            //Tant qu'il y a des données
                            while (myClientSocket.Available > 0)
                            {
                                try
                                {
                                    byte[] msg = new Byte[myClientSocket.Available];
                                    myClientSocket.Receive(msg, 0, myClientSocket.Available, SocketFlags.None);
                                    messageReceived = System.Text.Encoding.UTF8.GetString(msg).Trim();
                                    if (this.DataArrival != null) 
                                        this.DataArrival(true, new NetworkEventArgs(messageReceived));
                                }
                                catch (Exception e)
                                {
                                    if(this.ErrorOccured != null)
                                        this.ErrorOccured(true, new NetworkEventArgs("DataArrivalError: "+e));
                                }
                            }
                        }
                    }
                    //Permet d'évité que le processeur s'emballe :)
                    Thread.Sleep(10);
                }
            }
            catch
            {
                //Permet d'empecher l'excetpion
                if(this.Disconnected != null)
                    Disconnected(this, new NetworkEventArgs("forcedDisconnect"));
            }
        }
    }
}
